var COSA = COSA || {};

COSA.Core = COSA.Core || {};
/**
 * @memberOf COSA
 * @namespace
 */
COSA.Const = COSA.Const || {};
/**
 * @memberOf COSA
 * @namespace
 */
COSA.Response = COSA.Response || {};
/**
 * @memberOf COSA
 * @namespace
 */
COSA.Request = COSA.Request || {};
COSA.Util = COSA.Util || {};
COSA.Xml = COSA.Xml || {};


(function ( exports ) {
    var _profile = {};

    var _get_xhr = function () {
        return new XMLHttpRequest();
    };

    //Test
    var _get_context = function () {
        var context;
        if (typeof window.GetGlobalContext != "undefined") {
            context = window.GetGlobalContext();
        }
        else {
            if (typeof Xrm != "undefined") {
                context = Xrm.Page.context;
            }
            else if (typeof window.parent.Xrm != "undefined") {
                context = window.parent.Xrm.Page.context;
            }
            else {
                throw new Error("Context is not available.");
            }
        }
        return context;
    };

    //Test
    var _get_orgServicePath = function () {
        return _get_clientUrl() + '/XRMServices/2011/Organization.svc/web';
    };

    var _get_clientUrl = function () {
        var context = _get_context();
        var serverUrl = typeof context.getClientUrl != 'undefined' ? context.getClientUrl() : context.getServerUrl();
        if (serverUrl.match(/\/$/)) {
            serverUrl = serverUrl.substring(0, serverUrl.length - 1);
        }
        return serverUrl;
    };

    var _stringifyRequest = function ( requestObj ) {
        var resString = '';
        if ( Array.isArray(requestObj) ) {
            var sb = new COSA.Util.StringBuilder();
            requestObj.forEach(function ( req ) {
                var tmp = _stringifyNodes(req);
                sb.append(tmp);
            });
        } else {
            resString = _stringifyNodes(requestObj);
        }
        return resString;
    };

    var _stringifyNodes = function ( obj ) {
        var sb = new COSA.Util.StringBuilder();
        sb.concatAndAppend('<', obj.tag);
        for ( var attrName in obj.attr ) {
            sb.concatAndAppend(' ', attrName, '="', obj.attr[attrName], '"');
        }
        if ( (obj.hasOwnProperty('val') && obj.val !== null) || obj.hasOwnProperty('nodes') ) {
            sb.append('>');
            if ( obj.hasOwnProperty('nodes') ) {
                obj.nodes.forEach(function (node) {
                    var str = _stringifyNodes(node);
                    sb.append(str);
                });
            }
            if ( obj.hasOwnProperty('val') ) {
                if (typeof obj.val != 'string') {
                    sb.append(obj.val.toString());
                }
                else {
                    sb.append(obj.val);
                }
            }
            sb.concatAndAppend('</', obj.tag, '>');
        } else {
            sb.append(' />');
        }

        return sb.toString();
    };

    function SoapHeader( requestType, rqBody ) {
        return {
            tag: 'soap:Envelope',
            attr: {'xmlns:soap': 'http://schemas.xmlsoap.org/soap/envelope/'},
            nodes: [
                {
                    tag: 'soap:Body',
                    nodes: [{
                        tag: requestType,
                        attr: {
                            'xmlns': 'http://schemas.microsoft.com/xrm/2011/Contracts/Services',
                            'xmlns:i': 'http://www.w3.org/2001/XMLSchema-instance'
                        },
                        nodes: Array.isArray( rqBody ) ? rqBody : [rqBody]
                    }]
                }
            ]
        };
    }

    var _performRequest = function ( requestBodyStr, internalCallback ) {
        var xhr = _get_xhr();
        xhr.open('POST', _get_orgServicePath(), true);
        xhr.setRequestHeader('Accept', 'application/xml, text/xml, */*');
        xhr.setRequestHeader('Content-Type', 'text/xml; charset=utf-8');
        xhr.setRequestHeader('SOAPAction', 'http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute');

        xhr.onreadystatechange = function () {
            if ( xhr.readyState === 4 ) {
                xhr.onreadystatechange = null;
                internalCallback.apply(this, [ _handleResponse( xhr.responseXML, xhr.responseText ) ]);
            }
        };

        xhr.send(requestBodyStr);
    };

    var _handleResponse = function ( responseXml, responseText ) {
        var xmlDoc;

        if ( responseXml === null || typeof responseXml === 'undefined' || responseXml.xml === null || responseXml.xml === "" ) {
            if ( responseText !== null && responseText !== "" ) {
                throw new Error(responseText);
            } else {
                throw new Error("No response received from the server.");
            }
        }

        xmlDoc = typeof responseXml != 'undefined' ? responseXml : _parseXmlString(responseText);

        return xmlDoc;
    };

    var _parseXmlString = function ( xmlText ) {
        var xmlDoc = null;
        try {
            if ( window.DOMParser ) {
                var parser = new DOMParser();
                xmlDoc = parser.parseFromString(xmlText, 'text/xml');
            } else {
                xmlDoc = new ActiveXObjext('Microsoft.XMLDOM');
                xmlDoc.async = false;
                xmlDoc.loadXML(xmlText);
            }
        } catch ( e ) {
            throw new Error('Can\'t parse');
        }

        return xmlDoc;
    };

    exports.Init = function ( profile ) {
        if ( typeof profile != 'undefined' ) {
            _profile = profile;
        }
        else {
            _profile = {
                isDeepValidate: false
            };
        }
    };

    exports.Execute = function ( request, callback ) {
        var serializationContext = new COSA.SerializationContext(),
            requestXml, requestXmlString;

        try {
            if ( _profile.isDeepValidate ) {
                request.validate();
            }

            requestXml = new SoapHeader('Execute', (new COSA.Request.RequestWrapper(request)).serialize(serializationContext));
            requestXmlString = _stringifyRequest( requestXml );
        }
        catch ( e ) {
            console.error(e);
        }

        _performRequest(requestXmlString, function ( responseXml ) {
            var rsp;

            try {
                rsp = request.deserialize(new COSA.Xml.ExplorableNode(responseXml).node('Envelope.Body'));

                if ( typeof callback == 'function' ) {
                    callback.call(this, rsp);
                }
            }
            catch ( e ) {
                console.error(e);
            }
        });
    };

})(COSA.Core);
